/***************************************************************************/
/*                                                                         */
/* 234218 Data DSs 1, Winter 2012                                          */
/*                                                                         */
/* Homework : Wet 2                                                        */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/
/*                                                                         */
/* File Name : main2.cpp                                                   */
/*                                                                         */
/* Holds the "int main()" function and the parser of the shell's           */
/* command line.                                                           */
/***************************************************************************/


#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include "library2.h"

using namespace std;

#ifdef __cplusplus
extern "C" {
#endif




/* The command's strings */
typedef enum {
  NONE_CMD = -2,
  COMMENT_CMD = -1,
  INIT_CMD = 0,
  ADDCUBE_CMD = 1,
  PUTONTOP_CMD = 2,
  TOWERHEIGHT_CMD = 3,
  HEIGHTFROMGROUND_CMD = 4,
  TOWERSOFHEIGHTBETWEEN_CMD = 5,
  QUIT_CMD = 6
} commandType;



static const int   numActions   = 7;
static const char *commandStr[] = {
  "Init",
  "AddCube",
  "PutOnTop",
  "TowerHeight",
  "HeightFromGround",
  "TowersOfHeightBetween",
  "Quit"
};


static const char* ReturnValToStr(int val) {
	switch (val) {
		case (SUCCESS):          return "Success";
		case (FAILURE):          return "Failure";
		case (ALLOCATION_ERROR): return "Allocation_error";
		case (INVALID_INPUT):    return "Invalid_input";
		default:                 return "";
	}
}
	





/* we assume maximum string size is not longer than 256  */
#define MAX_STRING_INPUT_SIZE (255)
#define MAX_BUFFER_SIZE       (255)

#define StrCmp(Src1,Src2) ( strncmp((Src1),(Src2),strlen(Src1)) == 0 )

typedef enum {error_free, error} errorType;
static errorType parser(const char* const command);



#define ValidateRead(read_parameters,required_parameters,ErrorString) \
if ( (read_parameters)!=(required_parameters) ) { printf(ErrorString); return error; }


static bool isInit = false;


/* Print an array */
string PrintIntArray(const int* arr, int size) {
	char buffer[MAX_BUFFER_SIZE];
	string str = "";

	for (int i=0; i < size; i++) {
		sprintf(buffer,"%d",arr[i]);
		str += string(buffer) + ((i == (size - 1)) ? "" : ",");
	}
	return str.c_str();
}




/***************************************************************************/
/* main                                                                    */
/***************************************************************************/

int main() {
	int argc = 2;
	const char* argv[2] = {"", "in"};
    FILE* inputFile = NULL;

    setlocale(LC_ALL, "");
    if(argc == 2){
        inputFile = fopen(argv[1], "r");
        if (inputFile == NULL){
            printf("Problem opening file %s, make sure correct path name is given.\n", argv[1]);
            return 0;
        }
    }
    else {
        inputFile = stdin;
    }

    char buffer[MAX_STRING_INPUT_SIZE];
    // Reading commands
    while ( fgets(buffer, MAX_STRING_INPUT_SIZE, inputFile) != NULL ) {
        fflush(stdout);
        if ( parser(buffer) == error ){
            printf("ERROR\n");
            break;
        }
    };
    fclose(inputFile);
    return 0;
}

/***************************************************************************/
/* Command Checker                                                         */
/***************************************************************************/

static commandType CheckCommand(const char* const command, const char** const command_arg) {
  if ( command == NULL || strlen(command) == 0 || StrCmp("\n", command) )
    return(NONE_CMD);
  if ( StrCmp("#", command) ) {
    if (strlen(command) > 1)
      printf("%s", command);
    return(COMMENT_CMD);
  };
  for (int index=0; index < numActions; index++) {
    if ( StrCmp(commandStr[index], command) ) {
      *command_arg = command + strlen(commandStr[index]) + 1;
      return((commandType)index);
    };
  };
  return(NONE_CMD);
};

/***************************************************************************/
/* Commands Functions                                                      */
/***************************************************************************/

static errorType OnInit(void** DS, const char* const command);
static errorType OnAddCube(void* DS, const char* const command);
static errorType OnPutOnTop(void* DS, const char* const command);
static errorType OnTowerHeight(void* DS, const char* const command);
static errorType OnHeightFromGround(void* DS, const char* const command);
static errorType OnTowersOfHeightBetween(void* DS, const char* const command);
static errorType OnQuit(void** DS, const char* const command);




/***************************************************************************/
/* Parser                                                                  */
/***************************************************************************/

static errorType parser(const char* const command) { 
  static void *DS = NULL; /* The general data structure */
  const char* command_args = NULL;
  errorType rtn_val = error;

  commandType command_val = CheckCommand(command, &command_args);
 
  switch (command_val) {

	case (INIT_CMD):                   rtn_val = OnInit(&DS, command_args);	break;
	case (ADDCUBE_CMD):                rtn_val = OnAddCube(DS, command_args);	break;
	case (PUTONTOP_CMD):               rtn_val = OnPutOnTop(DS, command_args);	break;
	case (TOWERHEIGHT_CMD):            rtn_val = OnTowerHeight(DS, command_args);	break;
	case (HEIGHTFROMGROUND_CMD):       rtn_val = OnHeightFromGround(DS, command_args);	break;
	case (TOWERSOFHEIGHTBETWEEN_CMD):  rtn_val = OnTowersOfHeightBetween(DS, command_args);	break;
	case (QUIT_CMD):                   rtn_val = OnQuit(&DS, command_args);	break;
	
	case (COMMENT_CMD):                rtn_val = error_free;	break;
	case (NONE_CMD):                   rtn_val = error;	break;
	default: assert(false);
  };
  return(rtn_val);
};





/***************************************************************************/
/* OnInit                                                                  */
/***************************************************************************/
static errorType OnInit(void** DS, const char* const command) {
	if(isInit) {
		printf("Init was already called.\n");
		return(error_free);
	};
	isInit = true;

	int	sectionBNum;
	ValidateRead( sscanf(command, "%d" ,&sectionBNum), 1, "Init failed.\n" );
	
	bool sectionB;
	switch (sectionBNum) {
		case (1): sectionB = true;  break;
		case (0): sectionB = false; break;
		default:  printf("Init failed.\n"); return(error);
	}

	*DS = Init(sectionB);
	if( *DS == NULL ) {
		printf("Init failed.\n");
		return(error);
	}
	printf("Init done - TowersOfHeightBetween %s.\n",sectionB ? "enabled" :"disabled");

	return error_free;
}


/***************************************************************************/
/* OnAddCube                                                               */
/***************************************************************************/
static errorType OnAddCube(void* DS, const char* const command) {
	int height;
	int width;
	ValidateRead( sscanf(command, "%d %d",&height,&width), 2, "AddCube failed.\n" );
	StatusType res = AddCube(DS,height,width);
	
	printf("AddCube %d %d : %s\n",height,width,ReturnValToStr(res));

	return error_free;
}


/***************************************************************************/
/* OnPutOnTop                                                              */
/***************************************************************************/
static errorType OnPutOnTop(void* DS, const char* const command) {
	int cubeDown;
	int cubeUp;
	ValidateRead( sscanf(command, "%d %d",&cubeUp,&cubeDown), 2, "PutOnTop failed.\n" );
	StatusType res = PutOnTop(DS,cubeUp,cubeDown);
	
	printf("PutOnTop %d %d : %s\n",cubeUp,cubeDown,ReturnValToStr(res));

	return error_free;
}


/***************************************************************************/
/* OnTowerHeight                                                           */
/***************************************************************************/
static errorType OnTowerHeight(void* DS, const char* const command) {
	int cube;
	ValidateRead( sscanf(command, "%d",&cube), 1, "TowerHeight failed.\n" );
	int	height;
	StatusType res = TowerHeight(DS,cube,&height);
	
	if (res != SUCCESS) {
		printf("TowerHeight %d : %s\n",cube,ReturnValToStr(res));
	}
	else {
		printf("TowerHeight %d : %s %d\n",cube,ReturnValToStr(res),height);
	}

	return error_free;
}


/***************************************************************************/
/* OnHeightFromGround                                                      */
/***************************************************************************/
static errorType OnHeightFromGround(void* DS, const char* const command) {
	int cube;
	ValidateRead( sscanf(command, "%d",&cube), 1, "HeightFromGround failed.\n" );
	int	height;
	StatusType res = HeightFromGround(DS,cube,&height);
	
	if (res != SUCCESS) {
		printf("HeightFromGround %d : %s\n",cube,ReturnValToStr(res));
	}
	else {
		printf("HeightFromGround %d : %s %d\n",cube,ReturnValToStr(res),height);
	}

	return error_free;
}


/***************************************************************************/
/* OnTowersOfHeightBetween                                                 */
/***************************************************************************/
static errorType OnTowersOfHeightBetween(void* DS, const char* const command) {
	int min;
	int max;
	ValidateRead( sscanf(command, "%d %d",&min,&max), 2, "TowersOfHeightBetween failed.\n" );
	int	num;
	StatusType res = TowersOfHeightBetween(DS,min,max,&num);
	
	if (res != SUCCESS) {
		printf("TowersOfHeightBetween %d %d : %s\n",min,max,ReturnValToStr(res));
	}
	else {
		printf("TowersOfHeightBetween %d %d : %s %d\n",min,max,ReturnValToStr(res),num);
	}

	return error_free;
}


/***************************************************************************/
/* OnQuit                                                                  */
/***************************************************************************/
static errorType OnQuit(void** DS, const char* const command) {
	Quit(DS);
	if( *DS != NULL ) {
		printf("Quit failed.\n");
		return(error);
	};
	isInit = false;
	printf("Quit done.\n");

	return error_free;
}




#ifdef __cplusplus
}
#endif


